// 对外接口:
// 1. 设置 setConf()
// 2. 启动老虎机 boot()
// 3. 点停止 (选择哪个位置停止) stop()

class Slot {
  /**
   * 第 0, 1, 2 个插槽. 注意是从左往右数的！
   * 一旦确定不能改变的量
   */
  pos = 0;
  /**
   * 序列部分, 每个元素为 0-9 的整数转成的字符串
   * 比如 '0', '9' 等
   * @type {string[]}
   */
  seqs = [];
  /**
   * 如果序列发生了变动, 这里记录上一个序列遗留的数据
   * @type {string[]}
   */
  prevs = [];
  /**
   * 在 seqs 的索引, 如果索引在 prevs 上, 则为 -1
   */
  index = 0;
  /**
   * 老虎机的值转了百分之多少, 为 0
   */
  offset = 0;
  /**
   * 老虎机旋转速度. 最大 0.2
   */
  speed = 0;

  /**
   * @param {number} pos 
   * @param {Config} conf 
   * @param {string[]} constFigures
   *   已经确定哪些数字了. 可以理解为 char[3]
   */
  constructor (pos, conf, constFigures) {
    this.pos = pos;
    this.reset(conf, constFigures);
  };

  currentFigure () {
    if (this.prevs.length > 0) {
      return this.prevs[0];
    } else if (this.seqs.length == 0) {
      return '0';
    } else {
      return this.seqs[this.index];
    }
  };

  /**
   * 返回滚动的下一个数值或符号
   * @returns {string} '0', '1' 等
   */
  nextFigure () {
    if (this.prevs.length == 0) {
      let nextIndex = this.index + 1;
      if (nextIndex >= this.seqs.length) nextIndex = 0;
      return this.seqs[nextIndex];
    } else if (this.prevs.length == 1) {
      return (this.seqs.length == 0) ? '0' : this.seqs[0];
    } else {
      return this.prevs[1];
    }
  };

  isNextFigureShow () {
    return this.offset !== 0;
  };

  /**
   * @param {Config} conf 
   * @param {string[]} constFigures 
   */
  reset (conf, constFigures) {
    this.buildSeq(conf, constFigures);

    this.prevs = [];
    this.index = 0;
    this.offset = 0;
    this.speed = 0;
  };

  /**
   * 画面不进行抖动时 (原有老虎机的数字仍然写在上面),
   * 序列进行修改.
   * @param {Config} conf 
   * @param {string[]} constFigures 
   */
  hotReset (conf, constFigures) {
    if (this.offset == 0) {
      // prevs 长度为 1
      if (this.index < 0) {
        // 不变了
      } else {
        this.prevs = [this.seqs[this.index]];
      }
    } else {
      // prevs 长度为 2
      if (this.index < 0) {
        if (this.prevs.length == 2) {
          // 不变了
        } else {
          this.prevs.push(this.seqs[0] === undefined ? '0' : this.seqs[0]);
        }
      } else {
        let first = this.seqs[this.index];
        let nextIdx = this.index + 1;
        if (nextIdx >= this.seqs.length) nextIdx = 0;
        this.prevs = [first, this.seqs[nextIdx]];
      }
    }

    this.buildSeq(conf, constFigures.concat());
    this.index = -1;
  };

  /**
   * @param {Config} conf
   * @param {string[]} constFigures 
   * 如果一开始界面上已经显示的数字是 065, 那么这个是 ['0', '6', '5']
   */
  buildSeq (conf, constFigures) {
    if (constFigures == null) {
      constFigures = [null, null, null];
    }

    /** @type {Object<string, number>}} 统计每个数字出现的频率 */
    let figures = {};
    // figures.fill(0);
    let sum = 0;
    conf.candidates.forEach((candidate) => { // candidate: ConfigCandidate
      // 判断是不是
      for (let i = 0; i < 3; i++) {
        if (i === this.pos) continue;
        if (constFigures[i] != null && !candidate.figureEquals(constFigures, i)) {
          return;
        }
      }

      // 权重 10
      let weight = candidate.weight;
      let figure = candidate.slot[this.pos];
      if (figures[figure]) {
        figures[figure] += weight;
      } else {
        figures[figure] = weight;
      }
      sum += weight;
    });

    // 现在把所有的 key 选出来
    // 可能是 ['0', '1' ... '9']
    const keys = Object.keys(figures);

    this.seqs = new Array(sum);
    let index = 0;
    let figureIndex = 0;
    /** @type {string} */
    let currentFigure = keys[figureIndex];

    for (let remain = sum; remain > 0; remain--) {
      // 选数字
      while (figures[currentFigure] == 0) {
        figureIndex ++;
        currentFigure = keys[figureIndex];
      }

      // 找位置
      for (let skip = parseInt(remain * Math.random()); skip >= 0; skip--) {
        do {
          index ++;
          if (index >= sum) index = 0;
        } while (this.seqs[index] != null); // 找到 index, 使 seqs[index] == null
      }

      // 操作
      figures[currentFigure]--;
      this.seqs[index] = currentFigure;
    }
  };

  /**
   * 请不停地转. 如果速度小于 0.2 的逐渐增加速度, 直至 0.2
   * 该方法请每帧调一次.
   */
  roundTick () {
    // 调速
    if (this.speed < 0.2) {
      this.speed += (0.002 + Math.random() * 0.002);
      if (this.speed > 0.2) this.speed = 0.2;
    }

    this.offset += this.speed;
    if (this.offset > 1) {
      // 翻页
      this.turnPage();
    }
  };

  /**
   * 逐渐停止. 该方法请每帧调一次. 这里是, 每翻一页速度降一级.
   * 停下来的话, 返回 true
   * 
   * @param {boolean} stopable
   *   如果设置为 false, 它不能停.
   */
  stopTick (stopable) {
    stopable = (stopable == null) ? true : stopable;
    if (this.speed == 0) return true;

    this.offset += this.speed;
    if (this.offset > 1) {
      // 判断是否要停止，还是降速
      if (this.speed >= 0.01) {
        this.speed -= (0.003 + Math.random() * this.speed / 6);
        if (this.speed < 0.01) {
          if (this.prevs.length || (!stopable)) {
            this.speed = 0.01;
          } else {
            // 停下吧
            this.speed = 0;
            this.offset = 1;
          }
        }
      }

      // 翻页
      this.turnPage();
    }
    return this.speed === 0;
  };

  turnPage () {
    this.offset -= 1;
    if (this.index < 0) {
      this.prevs.splice(0, 1); // 删掉第一个值
      if (this.prevs.length == 0) {
        this.index = 0;
      }
    } else {
      this.index++;
      if (this.index >= this.seqs.length) this.index = 0;
    }
  }
}

export default Slot;
